﻿using System;
using System.ComponentModel.Composition;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using OrbitOne.CRMLyncInfoDashboard.Lync;

namespace OrbitOne.CRMLyncInfoDashboard.CRM.OData
{
    [Export(typeof(IPredicateBuilder))]
    public class PredicateBuilder : IPredicateBuilder
    {
        private MemberExpression GetMemberAccessExpression<T>(ParameterExpression parameterExpression, string fieldName)
        {
            return Expression.MakeMemberAccess(parameterExpression, typeof(T).GetMember(fieldName).First());
        }

        private ParameterExpression GetParameterExpression<T>()
        {
            return Expression.Parameter(typeof(T));
        }

        private MethodCallExpression GetMethodCallExpression(MemberExpression memberAccessExpression, MethodInfo method, object argument)
        {
            return Expression.Call(memberAccessExpression, method, Expression.Constant(argument));
        }

        public Expression<Func<T, bool>> GetPredicate<T>(string fieldName, MethodInfo method, params Endpoint[] arguments)
        {
            if (arguments.Length == 0) throw new ArgumentException("args");

            var parameterExpression = GetParameterExpression<T>();
            var memberAccessExpression = GetMemberAccessExpression<T>(parameterExpression, fieldName);

            Expression body = GetMethodCallExpression(memberAccessExpression, method, arguments[0].Uri);

            body = arguments.Skip(1)
                .Select(argument => GetMethodCallExpression(memberAccessExpression, method, argument.Uri))
                .Aggregate(body, Expression.OrElse);

            var fullPredicate = Expression.Lambda<Func<T, bool>>(body, parameterExpression);
            return fullPredicate;
        }
    }
}